-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local common_def = require 'common_def'
local method_misc = require 'method_misc'
local client = require 'storage.client'
local ctrl_commu_loss_monitor = require 'ctrl_commu_loss_monitor'
local c_storageconfig = require 'storageconfig.storageconfig_object'
local STORAGE_MGNT_STATE = common_def.STORAGE_MGNT_STATE
local log = require 'mc.logging'
local mctp_service = require 'mctp.mctp_service'
local debounce = require 'mc.debounce.debounce'
local skynet = require 'skynet'
local mdb = require 'mc.mdb'
local storage_bus = require 'storage_bus'
local controller_dump = require 'controller.controller_dump'
local cjson = require 'cjson'
local phy_err
local sml = require 'sml'
local signal = require 'mc.signal'
local c_object = require 'mc.orm.object'
local pd_object = require 'pd.pd_object'
local volume_obj_info = require 'volume.volume_obj_info'
local volume_collection = require 'volume.volume_collection'
local drive_collection = require 'drive.drive_collection'
local array_collection = require 'array.array_collection'
local battery_collection = require 'battery.battery_collection'
local CTRL_OPTION = common_def.CTRL_OPTION

local TASK_UPDATE<const> = 'update'
local TASK_UPDATE_PD_LIST<const> = 'update_pd_list'
local TASK_UPDATE_LD_LIST<const> = 'update_ld_list'
local TASK_UPDATE_PHY_ERR<const> = 'update_phy_err'

local VOLUME_TYPE_PHYSICAL_DRIVE_BIT<const> = 0x10000000
local VOLUME_TYPE_LOGICAL_DRIVE_BIT<const> = 0x20000000
local SUBARRAY_FORMAT<const> = 0x8000
local CONTROLLER_ID = 1

---@class c_controller: c_object
---@field Id integer
---@field TemperatureCelsius integer
---@field ControllerName string
---@field OOBSupport integer
---@field TypeId integer
---@field SystemId integer
---@field SocketId integer
---@field Segment integer
---@field DevBus integer
---@field DevDevice integer
---@field DevFunction integer
---@field CtrlOption1 integer
---@field CtrlOption2 integer
---@field CtrlOption3 integer
---@field Eid integer
---@field Phyaddr integer
---@field FaultCode integer
---@field BootDevices string[]
---@field RefDrives string[]
---@field RefChip table
---@field DeviceName string
---@field SlotId integer
---@field BatteryHealth integer
---@field on_add_pd c_basic_signal
---@field on_del_pd c_basic_signal
---@field on_update c_basic_signal
---@field new fun(...): c_controller
local c_controller = c_object('Controller', {'position'}) -- Controller 类，主键是 position 字段

function c_controller:ctor(obj)
    self.bus = storage_bus.get_instance().bus
    self.obj = obj
    obj.Eid = 0
    obj.Phyaddr = 0
    self.CryptoEraseSupported = false
    self.controller_path = string.match(obj.path, "Controller_%d+_%x+")

    self.pd_list = {}
    self.on_add_pd = signal.new()
    self.on_add_pd_complete = signal.new()
    self.on_del_pd = signal.new()

    self.volume_list = {}
    self.array_list = {}

    self.on_update = signal.new()

    self.on_update_volume_array = signal.new()

    self.is_working = false

    self.registered_state = false
    self.registering = false
    self.temp_debounce = debounce[debounce.DEBOUNCED_CONT].new(5, 0) -- 5 次防抖
    self.presence_debounce = debounce[debounce.DEBOUNCED_CONT].new(5, common_def.INVALID_U8) -- 5 次防抖
    self.tasks_pause = false
    self.first_register_task = false
    self.pcb_version = "N/A"
end

function c_controller:dtor()
    c_storageconfig.get_instance().controller_mask =
            c_storageconfig.get_instance().controller_mask ~ (1 << self.Id)
    self:stop()
end

local CONTROLLER_TYPE = {
    [0] = "SAS3108",
    [1] = "SAS3108",
    [2] = "SAS3108",
    [3] = "SAS3008",
    [4] = "SAS3008",
    [5] = "SAS3008",
    [6] = "SAS3508",
    [7] = "SAS3516",
    [8] = "SAS3408",
    [9] = "SAS3416",
    [10] = "SAS3408",
    [11] = "SAS3416",
    [12] = "SAS3004",
    [13] = "SAS3908",
    [14] = "SAS3908",
    [15] = "SAS3916",
    [16] = "SAS3808",
    [17] = "SAS3816",
    [18] = "SAS3808",
    [19] = "SAS3808",
    [20] = "SAS3816",
    [64] = "PM3152",
    [65] = "PM8222",
    [96] = 'B80121',
    [97] = 'B80121',
    [98] = 'B80121',
    [99] = 'B80121',
    [100] = 'B80121',
    [101] = 'B80121',
    [102] = 'B80121',
    [103] = 'B80121',
    [113] = 'B80121'
}

function c_controller:init()
    log:notice('controller init obj.Id = %s, object_id = %s', self.obj.Id, CONTROLLER_ID)
    self.obj.Id = (self.obj.Id == common_def.INVALID_U8) and CONTROLLER_ID - 1 or self.obj.Id
    CONTROLLER_ID = CONTROLLER_ID + 1
    self.super.init(self)
    ---@type c_signal_slot
    if not method_misc:test_controller_oob_support(self.TypeId) then
        self.Type = CONTROLLER_TYPE[self.TypeId]
    end
    self:set_default_values()
    self:set_default_volume_manage_values()
    self:set_default_health_status()
    self:set_default_battery_info()

    self.on_update:on(function(info)
        pcall(function()
            self:update_static_controller_info(info)
            self:update_controller_info(info)
            self:update_asset_data_info()
        end)
    end)

    self.on_update_volume_array:on(function()
        self:update_volume_list()
        self:update_array_list()
    end)

    if c_storageconfig.get_instance().power_on == 1 then
        self:register()
    end
    self.pcb_version = self:get_pcb_version(self.DeviceName)
end

function c_controller:get_pcb_version(device_name)
    local pcb_version = 'N/A'
    client:ForeachPCIeCardObjects(function(obj)
        if obj.DeviceName == device_name then
            pcb_version = obj.PcbVersion
        end
    end)
    return pcb_version
end

local function prefix_equal(str, prefix)
    return string.sub(str, 1, string.len(prefix)) == prefix
end

function c_controller.before_add_object(object)
    while true do
        skynet.sleep(10)
        -- 如果主键为空或Slot为255或Slot为0则不加载，等待有效主键分发
        if not prefix_equal(object.DeviceName, "PCIe Card") or
            prefix_equal(object.DeviceName, "PCIe Card $") or
            prefix_equal(object.DeviceName, "PCIe Card 0") or
            prefix_equal(object.DeviceName, "PCIe Card 255") then
            skynet.sleep(500)
        else
            break
        end
    end
end

function c_controller:fill_supported_raid_levels()
    local supported_raid_levels = {}
    if not self.CtrlOption3 then
        return supported_raid_levels
    end

    local level_array = {"0", "1", "5", "6", "10", "50", "60", "1(ADM)", "10(ADM)", "1Triple", "10Triple"};
    -- 从第8位开始
    local bit_val = 1 << 8
    for i = 1, #level_array do
        if bit_val & self.CtrlOption3 ~= 0 then
            table.insert(supported_raid_levels, 'RAID' .. level_array[i])
        end

        bit_val = bit_val << 1
    end

    return supported_raid_levels
end

-- 启动关联的更新任务
function c_controller:start_related_tasks()
    log:notice('controller%s start_related_tasks, registered_state = %s', self.Id, self.registered_state)
    if self.registered_state then
        self:start()
    else
        if not self.registering then
            self:register()
        end
    end
end

-- 关闭关联的更新任务
function c_controller:stop_related_tasks()
    log:notice('controller%s stop_related_tasks', self.Id)
    if self.registering then
        self.registering = false
    end
    self:stop()
    log:notice('controller%s set_default_values', self.Id)
    self:set_default_values()
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        log:notice('controller%s set_default_consistency_values', self.Id)
        self:set_default_consistency_values()
    end
    log:notice('controller%s set_default_volume_manage_values', self.Id)
    self:set_default_volume_manage_values()
    log:notice('controller%s set_default_health_status', self.Id)
    self:set_default_health_status()
    log:notice('controller%s set_default_battery_info', self.Id)
    self:set_default_battery_info()
    log:notice('controller%s set_default_phy_err', self.Id)
    self:set_default_phy_err()
    self:clear_asset_data_info()
end


-- 根据OS状态改变启停更新任务
function c_controller:change_tasks_state(os_state)
    log:notice('controller%s change task state to %s', self.Id, os_state and 'start' or 'stop')
    if os_state then
        self:start_related_tasks()
    else
        self:stop_related_tasks()
    end
end

local function record_commu_loss(ret, ctrl_index)
    -- 下电状态不算是通信丢失
    if c_storageconfig.get_instance().power_on ~= 1 then
        return
    end
    if ret ~= common_def.SML_ERR_I2C_READ_WRITE_FAILED then
        c_storageconfig.get_instance().configuration_ready_mask =
            c_storageconfig.get_instance().configuration_ready_mask ~ (1 << ctrl_index)
        c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_CTRL_ABNOMAL)
    end
    if ret ~= common_def.SML_ERR_CTRL_STATUS_INVALID and ret ~= common_def.SML_ERR_NULL_INFTERFACE then
        ctrl_commu_loss_monitor.get_instance():update(true, ctrl_index)
    end
end

function c_controller:register()
    log:notice('controller%s begin register', self.Id)
    self:new_task({ 'register_controller', self.Id }):loop(function(task)
        self.registering = true
        if c_storageconfig.get_instance().power_on == 0 then
            self.registering = false
            task:stop()
            return
        end

        if self.OOBSupport ~= 1 then
            c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_OOB_NOT_SUPPORT)
            self.registering = false
            task:stop()
            return
        end

        if method_misc:get_oob_interface_type(self.TypeId) == common_def.INVALID_U8 then
            log:error('[Storage]Invalid TypeId, controller_id = %d', self.Id)
            self.registering = false
            task:stop()
            return
        end

        if method_misc:get_oob_interface_type(self.TypeId) == common_def.OOB_TYPE_OVER_PCIE then
            if not mctp_service.get_instance().mctp_state then
                return
            end
            if self.DevBus == 0 and self.DevDevice == 0 and self.DevFunction == 0 then
                return
            end
        end

        self:add_controller_to_link_topo()
        log:notice('ctrl%s add_controller_to_link_topo successfully', self.Id)
        self:add_controller_to_sml()
        log:notice('ctrl%s add_controller_to_sml successfully', self.Id)
        self:register_controller_to_sml()
        log:notice('ctrl%s register_controller_to_sml successfully', self.Id)
        self.registering = false
        self.registered_state = true
        self:start()

        log:notice('controller_collection:add_object success, controller_id = %d', self.Id)
        self:trans_pd_vendor_data()
        task:stop()
    end):set_timeout_ms(5000)
end

function c_controller:update_temp_abnormal(ret)
    pcall(function ()
        if c_storageconfig.get_instance().obj and
            c_storageconfig.get_instance().obj.StorageConfigReady ==
            common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY then
            self.TemperatureAbnormal = self.temp_debounce:get_debounced_val(ret and 0 or 1)
        end
    end)
end

function c_controller:start_update_task()
    local update_phy_err_ok, update_phy_err_ret
    local id = tostring(self.Id)
    log:notice('controller%s start TASK_UPDATE', self.Id)
    self:new_task({TASK_UPDATE, id}):loop(function(task)
        if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
            return
        end
        update_phy_err_ok, update_phy_err_ret = pcall(sml.update_ctrl_init_state, self.Id)
        if not update_phy_err_ok then
            record_commu_loss(update_phy_err_ret, self.Id)
            return
        end
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        if update_phy_err_ret ~= 2 then
            return
        end
        if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
            return
        end
        -- 新增retry，规避sml.get_ctrl_info fail后，一直拿不到返回的控制器信息的情况。
        local err_count = 0
        ::continue::
        log:debug('controller%s start sml.get_ctrl_info %s times, typeid is %d', self.Id, err_count, self.TypeId)
        update_phy_err_ok, update_phy_err_ret = pcall(sml.get_ctrl_info, {Priority = 'Secondary'}, self.Id)
        self:update_temp_abnormal(update_phy_err_ok)
        if not update_phy_err_ok then
            if (self.TypeId == 19 or self.TypeId == 20) and err_count < 5 then
                err_count = err_count + 1
                goto continue
            end
            record_commu_loss(update_phy_err_ret, self.Id)
            return
        end
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        self.on_update:emit(update_phy_err_ret)
        c_storageconfig.get_instance().configuration_ready_mask =
            c_storageconfig.get_instance().configuration_ready_mask | (1 << self.Id)
        c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    end):set_timeout_ms(20000)
end

function c_controller:start_update_pd_list_task()
    local id = tostring(self.Id)
    log:notice('controller%s start TASK_UPDATE_PD_LIST', self.Id)
    self:new_task({TASK_UPDATE_PD_LIST, id}):loop(function(task)
        if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
            return
        end
        self:update_pd_list()
    end):set_timeout_ms(10000)
end

function c_controller:start_update_ld_list_task()
    local id = tostring(self.Id)
    if self.TypeId == 19 or self.TypeId == 20 then
        return
    end
    log:notice('controller%s start TASK_UPDATE_LD_LIST', self.Id)
    self:new_task({TASK_UPDATE_LD_LIST, id}):loop(function(task)
        if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
            return
        end
        self:update_array_list()
        self:update_volume_list()
    end):set_timeout_ms(60000)
end

function c_controller:start_update_phy_err_task()
    local id = tostring(self.Id)
    log:notice('controller%s start TASK_UPDATE_PHY_ERR', self.Id)
    self:new_task({TASK_UPDATE_PHY_ERR, id}):loop(function(task)
        if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
            return
        end
        self:update_phy_info()
    end):set_timeout_ms(60000)
end

function c_controller:start()
    log:notice('controller%s start update tasks', self.Id)
    self.tasks_pause = false
    if not self.first_register_task then
        log:notice('[Storage] first register task!!')
        self:start_update_task()
        self:start_update_pd_list_task()
        self:start_update_ld_list_task()
        self:start_update_phy_err_task()
        self.first_register_task = true
    end

    drive_collection.get_instance().on_controller_commu_changed:emit(true, self.Id)
    battery_collection.get_instance().on_controller_commu_changed:emit(true, self.Id)
    if not phy_err then
        phy_err = require('diagnose.phy_biterr')
    end
    phy_err.get_instance().on_controller_commu_changed:emit(true)
end

function c_controller:stop()
    log:notice('controller%s stop tasks', self.Id)
    self.tasks_pause = true
    drive_collection.get_instance().on_controller_commu_changed:emit(false, self.Id)
    battery_collection.get_instance().on_controller_commu_changed:emit(false, self.Id)
    -- 删除controller需要删除 array 列表并发送 on_del_array 信号
    self:remove_array_list()
    -- 删除controller需要删除 volume 列表并发送 on_del_volume 信号
    self:remove_volume_list()
end

-- 将值设为初始值
function c_controller:set_default_values()
    -- 对于不支持带外管理的RAID卡，CSR中配置的静态信息不清除
    if method_misc:test_controller_oob_support(self.TypeId) then
        self.ControllerName = common_def.INVALID_STRING
        self.Type = common_def.INVALID_STRING
    end
    self.FirmwareVersion = common_def.INVALID_STRING
    self.DeviceInterface = common_def.INVALID_STRING
    self.SASAddr = common_def.INVALID_STRING
    self.SerialNumber = common_def.INVALID_STRING
    self.MinStripSizeBytes, self.MaxStripSizeBytes = common_def.INVALID_U32, common_def.INVALID_U32
    self.MemorySizeMiB = common_def.INVALID_U16
    self.NVDataVersion = common_def.INVALID_STRING
    self.HardwareRevision = common_def.INVALID_STRING
    self.PCIeLinkWidth = common_def.INVALID_STRING
    self.TemperatureCelsius = common_def.INVALID_U8
    self.TemperatureAbnormal = 0
    self.SupportedMode = {common_def.INVALID_STRING}
    self.SupportedRAIDTypes = self:fill_supported_raid_levels()
    self.DDREccCount = common_def.INVALID_U16
    self.CachePinnedState = common_def.INVALID_U8
    self.MaintainPDFailHistrory = common_def.INVALID_U8
    self.WorkMode = common_def.INVALID_STRING
    self.JBODState = common_def.INVALID_U8
    self.CopyBackState = common_def.INVALID_U8
    self.SmarterCopyBackState = common_def.INVALID_U8
    self.BootDevices = {common_def.INVALID_STRING, common_def.INVALID_STRING}
    self.RefDrives = {}
    self.NoBatteryWriteCacheEnabled = common_def.INVALID_BOOLEAN
    self.ReadCachePercent = common_def.INVALID_U8
    self.DriverName = common_def.INVALID_STRING
    self.DriverVersion = common_def.INVALID_STRING
end

function c_controller:set_default_ctrl1_values()
    local string_pack = string.pack('I4', self.CtrlOption1)
    local opt1 = CTRL_OPTION.option1_format:unpack(string_pack)

    -- 读策略相关
    self.SupportedReadPolicyList = {}
    if opt1.no_read_ahead_opt == 1 then
        table.insert(self.SupportedReadPolicyList, common_def.READ_POLICY_LIST[0])
    end
    if opt1.read_ahead_opt == 1 then
        table.insert(self.SupportedReadPolicyList, common_def.READ_POLICY_LIST[1])
    end
    self.DefaultReadPolicy = common_def.READ_POLICY_LIST[opt1.read_policy_default]
    self.ReadPolicyWritable = opt1.read_policy_support == 0 and false or true

    -- 写策略相关
    self.SupportedWritePolicyList = {}
    if opt1.write_through_opt == 1 then
        table.insert(self.SupportedWritePolicyList, common_def.WRITE_PLOICY_LIST[0])
    end
    if opt1.write_back_opt == 1 then
        table.insert(self.SupportedWritePolicyList, common_def.WRITE_PLOICY_LIST[1])
    end
    if opt1.write_back_if_bad_bbu_opt == 1 then
        table.insert(self.SupportedWritePolicyList, common_def.WRITE_PLOICY_LIST[2])
    end
    self.DefaultWritePolicy = common_def.WRITE_PLOICY_LIST[opt1.write_policy_default]
    self.WritePolicyWritable = opt1.write_policy_support == 0 and false or true

    -- IO策略相关
    self.SupportedIOPolicyList = {}
    if opt1.cached_io_opt == 1 then
        table.insert(self.SupportedIOPolicyList, common_def.IO_PLOICY_LIST[0])
    end
    if opt1.direct_io_opt == 1 then
        table.insert(self.SupportedIOPolicyList, common_def.IO_PLOICY_LIST[1])
    end
    self.DefaultIOPolicy = common_def.IO_PLOICY_LIST[opt1.io_policy_default]
    self.IOPolicyWritable = opt1.io_policy_support == 0 and false or true

    -- 访问策略相关
    self.SupportedAccessPolicyList = {}
    if opt1.access_rw_opt == 1 then
        table.insert(self.SupportedAccessPolicyList, common_def.ACCESS_POLICY_LIST[0])
    end
    if opt1.access_read_only_opt == 1 then
        table.insert(self.SupportedAccessPolicyList, common_def.ACCESS_POLICY_LIST[1])
    end
    if opt1.access_blocked_opt == 1 then
        table.insert(self.SupportedAccessPolicyList, common_def.ACCESS_POLICY_LIST[2])
    end
    self.DefaultAccessPolicy = common_def.ACCESS_POLICY_LIST[opt1.access_policy_default]
    self.AccessPolicyWritable = opt1.access_policy_support == 0 and false or true
end

function c_controller:set_default_ctrl2_values()
    local string_pack = string.pack('I4', self.CtrlOption2)
    local opt2 = CTRL_OPTION.option2_format:unpack(string_pack)

    self.SupportedDriveCachePolicyList = {}
    if opt2.pd_cache_unchanged_opt == 1 then
        table.insert(self.SupportedDriveCachePolicyList, common_def.DRIVE_CACHE_POLICY_LIST[0])
    end
    if opt2.pd_cache_enable_opt == 1 then
        table.insert(self.SupportedDriveCachePolicyList, common_def.DRIVE_CACHE_POLICY_LIST[1])
    end
    if opt2.pd_cache_disable_opt == 1 then
        table.insert(self.SupportedDriveCachePolicyList, common_def.DRIVE_CACHE_POLICY_LIST[2])
    end
    self.DefaultDriveCachePolicy = common_def.DRIVE_CACHE_POLICY_LIST[opt2.pd_cache_policy_default]
    self.DriveCachePolicyWritable = opt2.pd_cache_policy_support == 0 and false or true
    self.EpdSupported = opt2.ctrl_support_epd ~= 0 and true or false
    self.JbodSupported = opt2.ctrl_support_jbod_state ~= 0 and true or false
end

function c_controller:set_default_ctrl3_values()
    local string_pack = string.pack('I4', self.CtrlOption3)
    local opt3 = CTRL_OPTION.option3_format:unpack(string_pack)

    self.ConfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.configured_drive_wcp] or ''
    self.UnconfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.unconfigured_drive_wcp] or ''
    self.HBADriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.hba_drive_wcp] or ''
end

-- 解析VPD上的值来初始化
function c_controller:set_default_volume_manage_values()
    self.CreateVolumeSupported = false
    self:set_default_ctrl1_values()
    self:set_default_ctrl2_values()
    self:set_default_ctrl3_values()
end

function c_controller:set_default_consistency_values()
    self.ConsistencyCheckState = common_def.INVALID_BOOLEAN
    self.Rate = common_def.INVALID_STRING
    self.PeriodOfHours = common_def.INVALID_U16
    self.AutoRepairEnabled = common_def.INVALID_BOOLEAN
    self.RunningStatus = common_def.INVALID_STRING
    self.TotalVolumeCounts = common_def.INVALID_U16
    self.CompletedVolumeCounts = common_def.INVALID_U16
end

function c_controller:set_default_health_status()
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    self.FaultCode = common_def.INVALID_U16
    self.CommunicationLoss = 0
end

function c_controller:set_default_battery_info()
    self.BatteryFault = 0
end

function c_controller:set_default_phy_err()
    local phy_children = self:get_phy_children()
    for _, v in pairs(phy_children) do
        v.PhyId = common_def.INVALID_U8
        v.InvalidDwordCount = 0
        v.LossDwordSyncCount = 0
        v.PhyResetProblemCount = 0
        v.RunningDisparityErrorCount = 0
    end
end

local function generate_controller_health_maintain_log(controller, ret)
    local last_fault_code = controller.FaultCode == common_def.INVALID_U16 and 0 or controller.FaultCode
    local cur_fault_code = ret.health_status_code
    local tmp = last_fault_code ~ cur_fault_code
    if tmp & 1 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_CE,
            "RAID controller (%s) detected memory correctable errors - %s.",
            controller.DeviceName, cur_fault_code & 1 ~= 0 and 'Asserted' or 'Deasserted')
    end
    if tmp & 1 << common_def.BIT_OFFSET_MEMORY_CORRECTABLE_ERROR ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_UCE,
            "RAID controller (%s) detected memory uncorrectable errors - %s.", controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_MEMORY_CORRECTABLE_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end
    if tmp & 1 << common_def.BIT_OFFSET_MEMORY_UNCORRECTABLE_ERROR ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_ECC,
            "RAID controller (%s) detected memory ECC errors reaching limit - %s.", controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_MEMORY_UNCORRECTABLE_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end
    if tmp & 1 << common_def.BIT_OFFSET_ECC_ERROR ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_NVRAM,
            "RAID controller (%s) detected NVRAM errors - %s.", controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_ECC_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_HW_ERROR ~= 0 or ret.err_change & 2 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_BOARD_HARDWARE,
            "RAID controller (%s) detected BOARD_HW errors, err_code: 0x%s - %s.",
            controller.DeviceName, string.format('%x', ret.hw_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_HW_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_CLK_ERROR ~= 0 or ret.err_change & 4 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_BOARD_CLOCK,
            "RAID controller (%s) detected BOARD_CLK errors, err_code: 0x%s - %s.",
            controller.DeviceName, string.format('%x', ret.clk_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_CLK_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_POWER_ERROR ~= 0 or ret.err_change & 8 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_BOARD_POWER,
            "RAID controller (%s) detected BOARD_POWER errors, err_code: 0x%s - %s.",
            controller.DeviceName, string.format('%x', ret.power_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_POWER_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_CAPACITY_ERROR ~= 0 or ret.err_change & 16 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_BOARD_CAPACITY,
            "RAID controller (%s) detected BOARD_CAPACITY errors, err_code: 0x%s - %s.",
            controller.DeviceName, string.format('%x', ret.capacity_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_CAPACITY_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end
    if tmp & 1 << common_def.BIT_OFFSET_RAID_FLASH_ERROR ~= 0 or ret.err_change & 32 ~= 0 then
        log:maintenance(log.MLOG_ERROR, common_def.FC_STORAGE_RAID_BOARD_FLASH,
            "RAID controller (%s) detected FLASH errors, err_code: 0x%s - %s.",
            controller.DeviceName, string.format('%x', ret.flash_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_FLASH_ERROR ~= 0 and 'Asserted' or 'Deasserted')
    end
end

-- 在通信链路中添加控制器对象
function c_controller:add_controller_to_link_topo()
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_I2C then
        local ok, _ = sml.set_i2c_chip(self.Id, self.RefChip)
        if not ok then
            error('[Storage]Failed to set i2c chip, controller_id = %d', self.Id)
        end
        log:notice('Controller_%d, RefChip.Path:%s', self.Id, self.RefChip.path)
    elseif method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        local ok, ret = pcall(mctp_service.get_instance().register_mctp_object, mctp_service.get_instance(), self)
        if not ok then
            error(string.format('[Storage]Failed to register mctp object, ret = %s', ret))
        end
        log:notice('Register Controller_%d to mctp successfully', self.Id)
    end
end

-- 在smlib中添加控制器对象
function c_controller:add_controller_to_sml()
    local data = {}
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        data = {
            Eid = self.Eid,
            Phyaddr = self.Phyaddr
        }
    end

    local ret = sml.register_controller(self.Id, self.TypeId, sml.SML_ADD_CTRL, data)
    if ret ~= 0 then
        if ret ~= common_def.SML_ERR_CTRL_DUPLICATE_REGISTERED then
            ctrl_commu_loss_monitor.get_instance():update(true, self.Id)
        end
        error(string.format('[Storage] Failed to add controller %d, ret: %s', self.Id, ret))
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
end

-- 将控制器管理对象注册给smlib
function c_controller:register_controller_to_sml()
    local data = {}
    local type_id_param = self:get_mapping_type_id()
    local ret = sml.register_controller(self.Id, type_id_param, sml.SML_REGISTER_CTRL, data)
    if ret ~= 0 then
        if ret ~= common_def.SML_ERR_CTRL_DUPLICATE_REGISTERED then
            ctrl_commu_loss_monitor.get_instance():update(true, self.Id)
        end
        error(string.format('[Storage] Failed to register controller %d, ret: %s', self.Id, ret))
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
end

local function get_state(v)
    return (v == 0 or v == 1) and v or 2
end

local function set_suitable_state(v)
    return (v == 0 or v == 1) and v or common_def.INVALID_U8
end

local function str_or_na(v)
    if not v or #v == 0 then
        return common_def.INVALID_STRING
    end
    return v
end

-- 更新控制器静态对象信息
---@param info c_ctrl_info
function c_controller:update_static_controller_info(info)
    self:update_controller_info_via_bma()
    self.ControllerName = info.ctrl_name
    self.Type = CONTROLLER_TYPE[self.TypeId]
    self.FirmwareVersion = info.fw_version
    self.DeviceInterface = info:get_ctrl_device_interface()
    local ok, ret = pcall(sml.get_ctrl_sas_addr, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        self.SASAddr = ret
    else
        record_commu_loss(ret, self.Id)
    end
    self.SerialNumber = info.ctrl_sn
    self.MinStripSizeBytes, self.MaxStripSizeBytes = info:get_ctrl_strip_szie_options()
    self.MemorySizeMiB = method_misc:update_if_valid(0, info.memory_size, common_def.INVALID_U16)
    self.NVDataVersion = str_or_na(info.nvdata_version)
    self.HardwareRevision = str_or_na(info.hardware_revision)
    self.PCIeLinkWidth = (info.pcie_link_width and info.pcie_link_width ~= common_def.INVALID_U8) and
        string.format('x%d', info.pcie_link_width) or common_def.INVALID_STRING
    self.SupportedMode = info.work_mode
    self.HotSpareActivationMode = common_def.ACTIVATION_MODE[info.spare_activation_mode] or ''
    self.SupportedRAIDTypes = info:get_raid_types()
    self.NoBatteryWriteCacheEnabled = get_state(info.nobattery_write_cache)
    self.ReadCachePercent = info.read_cache_percent
end

function c_controller:generate_boot_device_desc(volume_id)
    if volume_id == 0 then
        return 'None'
    end
    if volume_id & VOLUME_TYPE_PHYSICAL_DRIVE_BIT ~= 0 then
        local drive_obj = drive_collection:get_drive_by_physic_id(self.Id, volume_id & 0xFF,
            (volume_id & 0xFFFF00) >> 8)
        if drive_obj then
            return drive_obj.Name
        end
    end
    if volume_id & VOLUME_TYPE_LOGICAL_DRIVE_BIT ~= 0 then
        local ret = 'Volume' .. (volume_id & 0xFFFF)
        return ret
    end
end

local rate_array = {'Low', 'Medium', 'High'}
local running_state = {'Off', 'On'}
local work_mode = {'RAID', 'HBA', 'JBOD', 'Mixed'}
function c_controller:update_consistency_check(consis_check_table)
    self.ConsistencyCheckState =
        get_state(consis_check_table.o_consis_check_enabled)
    self.PeriodOfHours = consis_check_table.o_consis_check_period
    self.Rate = rate_array[consis_check_table.o_consis_check_rate]
    self.AutoRepairEnabled = get_state(consis_check_table.o_consis_check_repair)
    self.RunningStatus = running_state[consis_check_table.o_consis_check_status + 1]
    self.TotalVolumeCounts = consis_check_table.o_consis_check_totalvd
    self.CompletedVolumeCounts = consis_check_table.o_consis_check_completedvd
end

function c_controller:update_controller_drives_policy(info)
    local ctrl_operations = info:get_support_operations().ctrl_operations

    self.HBADriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_hba_drive_wcp] or ''
    self.ConfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_configured_drive_wcp] or ''
    self.UnconfiguredDriveWriteCachePolicy =
        common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_unconfigured_drive_wcp] or ''
    self.EpdSupported = ctrl_operations.o_support_epd ~= 0 and true or false
    self.JbodSupported = ctrl_operations.o_support_jbod_state ~= 0 and true or false
end

-- 更新控制器对象信息
---@param info c_ctrl_info
function c_controller:update_controller_info(info)
    -- 处理某些厂商raid卡核温为负的场景: 将16位无符号数转化为有符号数，如65535转成-1
    local two_bytes_width = info.ctrl_temp & common_def.U16_MAX
    local trans_temperature = two_bytes_width >= common_def.SIGNED_BIT and
        (-(common_def.U16_MAX - two_bytes_width + 1)) or two_bytes_width
    self.TemperatureCelsius = (trans_temperature >= 255) and 255 or trans_temperature
    self.DDREccCount = info.memory_ecc_count
    self.CachePinnedState = set_suitable_state(info.cache_pinned)
    self.MaintainPDFailHistrory = set_suitable_state(info.maint_pd_fail_history)
    self.WorkMode = (info.mode and info.mode < #work_mode) and
        work_mode[info.mode + 1] or common_def.INVALID_STRING

    self:update_controller_drives_policy(info)
    local support_properties = info:get_support_properties(self.TypeId)
    self.JBODState = set_suitable_state(support_properties.o_jbod_enabled)
    self.CopyBackState = set_suitable_state(support_properties.o_copyback_enabled)
    self.SmarterCopyBackState = set_suitable_state(support_properties.o_smarter_copyback_enabled)
    self.CryptoEraseSupported = get_state(support_properties.o_support_crypto_erase) == 0 and false or true
    self.CreateVolumeSupported = c_storageconfig.get_instance():get_set_configuration_ready_flag(self.Id)

    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        self:update_consistency_check(support_properties)
    end

    local ok, ret1, ret2 = pcall(sml.get_ctrl_boot_devices, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        local primary_device = self:generate_boot_device_desc(ret1)
        local secondary_device = self:generate_boot_device_desc(ret2)
        self.BootDevices = {primary_device, secondary_device}
    else
        record_commu_loss(ret1, self.Id)
    end

    ok, ret1 = pcall(sml.get_ctrl_faultcode, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        generate_controller_health_maintain_log(self, ret1)
        self.FaultCode = ret1.health_status_code
    else
        record_commu_loss(ret1, self.Id)
    end

    self.CommunicationLoss = ctrl_commu_loss_monitor.get_instance():get_ctrl_commu_loss(self.Id)

    self.RefDrives = drive_collection.get_instance():get_drives_by_controller_id(self.Id)
    drive_collection.get_instance():update_drive_ref_controller_type_id(self.Id, self.TypeId)

    self:update_controller_battery_info()
end

function c_controller:update_controller_battery_info()
    local ref_battery = battery_collection.get_instance():get_capcacitance_by_controller_id(self.Id)
    if not ref_battery then
        return
    end
    self.BatteryPresence = self.presence_debounce:get_debounced_val(ref_battery.State and
        ref_battery.State or common_def.INVALID_U8)
    self.BatteryFault = (ref_battery.Fault == 0 and 0 or 1)
    ref_battery.Health = self.BatteryHealth
end

-- 比较控制器 pd_list，计算新增和删除的 pd 列表
function c_controller:compare_pd_list(pd_list)
    local new_pd_list = {}
    local del_pd_list = {}
    local pd_count = 0
    for _, pd in ipairs(pd_list) do
        if not self.pd_list[pd.key] then
            new_pd_list[#new_pd_list + 1] = pd
        end
        new_pd_list[pd.key] = true
    end

    for key in pairs(self.pd_list) do
        pd_count = pd_count + 1
        if not new_pd_list[key] then
            del_pd_list[#del_pd_list + 1] = key
        end
    end

    local pd_count_changed = (#pd_list == pd_count)
    return pd_count_changed, new_pd_list, del_pd_list
end

-- 获取控制器 pd_list
function c_controller:get_ctrl_pd_list()
    local pd_list = {}
    local controller_id = self.Id
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    local ok, ret = pcall(sml.get_ctrl_pd_list, controller_id)
    if not ok then
        log:error('get_ctrl_pd_list failed and return %s', ret)
        record_commu_loss(ret, self.Id)
        return nil
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    for _, pd in ipairs(ret) do
        pd_list[#pd_list + 1] = pd_object.new(controller_id, pd, self.TypeId)
    end
    table.sort(pd_list, function(a, b)
        return a.slot_num < b.slot_num
    end)
    return pd_list
end

-- 更新控制器 pd_list
function c_controller:update_pd_list()
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end

    local pd_list = self:get_ctrl_pd_list()
    if pd_list == nil then
        return
    end
    local pd_count_changed, new_pd_list, del_pd_list = self:compare_pd_list(pd_list)

    for _, key in ipairs(del_pd_list) do
        local deleted_pd = self.pd_list[key]
        local drive = drive_collection.get_instance():get_drive_by_physic_id(self.Id, deleted_pd.slot_num,
            deleted_pd.enclosure_id)
        if drive then
            drive:check_pd_missing(pd_count_changed, pd_list)
        end
        self.pd_list[key] = nil

        deleted_pd:invalid()
        self.on_del_pd:emit(deleted_pd)
    end

    for _, pd in ipairs(new_pd_list) do
        self.pd_list[pd.key] = pd
        self.on_add_pd:emit(pd)
    end
    self.on_add_pd_complete:emit(true)
end

function c_controller:get_sub_objs(path)
    local service = 'bmc.kepler.host_agent'
    local bus_resource = self.bus
    for _ = 1, 3 do
        local err, objects = bus_resource:pcall(service, path,
            'org.freedesktop.DBus.ObjectManager', 'GetManagedObjects', '')
            if not err and objects then
                return objects
            end
        skynet.sleep(100)
    end
end

-- 通过BMA更新raid
function c_controller:update_controller_info_via_bma()
    local ok_sms_status, sms_status_obj =
        pcall(mdb.get_object, self.bus, '/bmc/kepler/Systems/1/Sms', 'bmc.kepler.Systems.Sms.SmsStatus')
    local ok_sms, sms_obj =
        pcall(mdb.get_object, self.bus, '/bmc/kepler/Systems/1/Sms', 'bmc.kepler.Systems.Sms')

    if not ok_sms_status or not ok_sms then
        return
    end

    if sms_status_obj.State == 0 and sms_obj.Registered == true then
        self:update_info_via_bma()
    else
        self.DriverName = common_def.INVALID_STRING
        self.DriverVersion = common_def.INVALID_STRING
    end
end

local CONTROLLER_PATH = '/bmc/kepler/Systems/1/Sms/1/ComputerSystem/Systems/1/Storage/1/StorageControllers'

local function convert_bdf(bdf)
    if not bdf then
        error('no bdf')
    end
    return string.match(bdf, '(.+):(.+):(.+).(.+)')
end

local BDF_PATH = 'bmc.kepler.sms.redfish.Oem.Huawei.BDFNumber'
function c_controller:bdf_match(bma_resource)
    if not bma_resource[BDF_PATH] or not bma_resource[BDF_PATH].BDF then
        log:error('Invalid BDF_PATH')
        return false
    end
    local ok, _, dev_bus, dev_device, dev_func = pcall(convert_bdf, bma_resource[BDF_PATH].BDF:value():value())
    if not ok then
        log:error('Failed to convert BDF')
        return false
    end
    if self.DevBus == tonumber(dev_bus, 16) and
        self.DevDevice == tonumber(dev_device, 16) and
        self.DevFunction == tonumber(dev_func, 16) then
        return true
    end
    return false
end

local function update_drive_info_from_bma(driveinfo)
    local driveinfo_str = common_def.INVALID_STRING
    if driveinfo ~= 'null' then
        driveinfo_str = driveinfo
    end
    return driveinfo_str
end

function c_controller:update_info_via_bma()
    local info_interface = 'bmc.kepler.sms.redfish.Oem.Huawei.DriverInfo'
    local objects = self:get_sub_objs(CONTROLLER_PATH)
    if not objects then
        return
    end
    for _, object in pairs(objects) do
        if self:bdf_match(object) then
            self.DriverName = update_drive_info_from_bma(object[info_interface].DriverName:value():value())
            self.DriverVersion = update_drive_info_from_bma(object[info_interface].DriverVersion:value():value())
            return
        end
    end
    log:notice('Controller %s update DriverName and DriverVersion failed!', self.Id)
end

-- 比较控制器 volume_list，计算新增和删除的 volume 列表
function c_controller:compare_volume_list(volume_list)
    local new_volume_list = {}
    local del_volume_list = {}
    for _, volume in ipairs(volume_list) do
        if not self.volume_list[volume.key] then
            new_volume_list[#new_volume_list + 1] = volume
        end
        new_volume_list[volume.key] = true
    end

    for key in pairs(self.volume_list) do
        if not new_volume_list[key] then
            del_volume_list[#del_volume_list + 1] = key
        end
    end

    if log:getLevel() >= log.INFO then
        log:info('Controller %s add new volume list:%s, del volume list:%s', self.Id, cjson.encode(new_volume_list),
            cjson.encode(del_volume_list))
    end
    return new_volume_list, del_volume_list
end

-- 获取控制器 volume_list
function c_controller:get_ctrl_volume_list()
    local volume_list = {}
    local controller_id = self.Id
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    local ok, ret = pcall(sml.get_ctrl_ld_list, controller_id)
    if not ok then
        log:error('get_ctrl_ld_list failed and return %s', ret)
        record_commu_loss(ret, self.Id)
        return nil
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    for _, volume_id in ipairs(ret) do
        volume_list[#volume_list + 1] = volume_obj_info.new(self.position, controller_id, volume_id)
    end
    return volume_list
end

-- 更新控制器 volume_list
function c_controller:update_volume_list()
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end
    local volume_list = self:get_ctrl_volume_list()
    if volume_list == nil then
        return
    end
    local new_volume_list, del_volume_list = self:compare_volume_list(volume_list)

    for _, key in ipairs(del_volume_list) do
        local deleted_volume = self.volume_list[key]
        self.volume_list[key] = nil
        volume_collection.get_instance().on_del_volume:emit(deleted_volume)
    end

    for _, volume in ipairs(new_volume_list) do
        self.volume_list[volume.key] = volume
        volume_collection.get_instance().on_add_volume:emit(volume)
    end
end

function c_controller:remove_volume_list()
    local old_volume_list = self.volume_list
    self.volume_list = {}

    for _, volume_obj in pairs(old_volume_list) do
        volume_collection.get_instance().on_del_volume:emit(volume_obj)
    end
end

-- 比较控制器 array_list，计算新增和删除的 array 列表
function c_controller:compare_array_list(sml_array_list)
    local add_array_list = {}
    local del_array_list = {}
    local new_array_list = {}
    for _, array_id in ipairs(sml_array_list) do
        if not self.array_list[array_id] then
            add_array_list[#add_array_list + 1] = array_id
        end
        new_array_list[array_id] = true
    end
    for array_id, _ in pairs(self.array_list) do
        if not new_array_list[array_id] then
            del_array_list[#del_array_list + 1] = array_id
        end
    end
    return add_array_list, del_array_list
end

-- 更新控制器的硬盘阵列信息
function c_controller:update_array_list()
    local controller_id = self.Id
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    -- 从RAID卡获取硬盘阵列列表
    ok, ret = pcall(sml.get_ctrl_array_list, controller_id)
    if not ok then
        record_commu_loss(ret, self.Id)
        return
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    -- 从硬盘阵列服务获取硬盘阵列列表
    self.array_list = array_collection.get_instance():get_ctrl_array_list_by_ctrl_id(self.Id)

    local add_array_list, del_array_list = self:compare_array_list(ret)

    for _, array_id in ipairs(del_array_list) do
        self.array_list[array_id] = nil
        array_collection.get_instance().on_del_array:emit(self.Id, array_id)
    end

    for _, array_id in ipairs(add_array_list) do
        self.array_list[array_id] = true
        array_collection.get_instance().on_add_array:emit(self.Id, self.controller_path, array_id)
    end
end

function c_controller:remove_array_list()
    local old_array_list = self.array_list
    self.array_list = {}

    for array_id, _ in pairs(old_array_list) do
        array_collection.get_instance().on_del_array:emit(self.Id, array_id)
    end
end

function c_controller:get_phy_children()
    local children = self:get_children()
    local phy_children = {}
    for _, v in pairs(children) do
        if v['PhyId'] then
            table.insert(phy_children, v)
        end
    end

    if next(phy_children) then
        table.sort(phy_children, function(a, b)
            return a.path < b.path
        end)
    end

    return phy_children
end

function c_controller:update_phy_info()
    local ok, phy_addr = pcall(function()
        return sml.get_ctrl_sas_phy_err(self.Id, 0)
    end)

    if not ok or not phy_addr then
        return
    end

    local phy_children = self:get_phy_children()
    -- #计算从下标1开始，phy_addr数组从下标0开始，因此+1
    if #phy_addr + 1 ~= #phy_children then
        return
    end

    local phy_id
    for k, v in pairs(phy_children) do
        phy_id = k - 1
        v.PhyId = phy_id
        v.InvalidDwordCount = phy_addr[phy_id].invalid_dword_count
        v.LossDwordSyncCount = phy_addr[phy_id].loss_dword_sync_count
        v.PhyResetProblemCount = phy_addr[phy_id].phy_reset_problem_count
        v.RunningDisparityErrorCount = phy_addr[phy_id].running_disparity_error_count
    end
end

function c_controller:dump_info(fp_w)
    controller_dump:dump_info(fp_w, self)
end

-- 清除资产信息
function c_controller:clear_asset_data_info()
    self.AssetType = ''
    self.AssetName = ''
    self.InventorySerialNumber = ''
    self.InventoryFirmwareVersion = ''
    self.PCBVersion = ''
    self.Manufacturer = ''
    self.AssetTag = ''
    self.PartNumber = ''
    self.ManufactureDate = ''
    self.Slot = ''
    self.UUID = ''
    self.InventoryModel = ''
end

-- 收集资产清单
function c_controller:update_asset_data_info()
    self.AssetType = 'PCIe RAID Card'
    self.AssetName = self.ControllerName
    self.InventorySerialNumber = self.SerialNumber
    self.InventoryFirmwareVersion = self.FirmwareVersion
    self.PCBVersion = self.pcb_version
    self.Manufacturer = self.ChipManufacturer
    self.InventoryModel = self.Model
    self.AssetTag = 'N/A'
    self.PartNumber = self.BOMNumber
    self.ManufactureDate = 'N/A'
    self.Slot = tostring(self.Id)
    self.UUID = 'N/A'
end

-- 传递硬盘厂商数据
function c_controller:trans_pd_vendor_data()
    local data_str = cjson.encode(common_def.DRIVE_VENDOR_TABLE)
    local ok, ret = pcall(sml.trans_drive_data, self.Id, data_str)
    if not ok then
        log:error("trans pd_vendor_data failed ,ret is %s", ret)
    end
end

-- 将新配置的raid卡的TypeId映射到已有的TypeId上
function c_controller:get_mapping_type_id()
    return common_def.TYPEID_MAPPING[self.TypeId] and common_def.TYPEID_MAPPING[self.TypeId] or self.TypeId
end

c_controller.record_commu_loss = record_commu_loss

return c_controller
